package pers.whj.net.local.server;

import java.io.File;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;

public class Server {
    protected final Object LOCK;
    protected final Map<Long, ServerThread> mServerMap;
    protected int mClientCounts;
    private long mCurrentID;
    private String mRootPath;
    private int mPort;
    private boolean mEndFlag;
    private ServerSocket mServerSocket;
    private UDPThread mUDPThread;
    private CreateServerThreadListener mCreateServerThreadListener;
    private EndServerThreadListener mEndServerThreadListener;

    public Server(String rootPath) {
        mRootPath = "C:\\";
        mPort = 0;
        mClientCounts = 0;
        mCurrentID = 1;
        mEndFlag = false;
        mServerMap = new HashMap<>();
        LOCK = new Object();
        setRootPath(rootPath);
    }

    public final void start() {
        try {
            mServerSocket = new ServerSocket(mPort);
            mPort = mServerSocket.getLocalPort();
            mUDPThread = new UDPThread(mPort);
            mUDPThread.start();
            do {
                Socket client = mServerSocket.accept();
                ServerThread serverThread = createServerThread(client);
                if (serverThread != null) {
                    if (mCreateServerThreadListener != null) {
                        mCreateServerThreadListener.onServerThreadCreate(serverThread.getID());
                    }
                    serverThread.setEndServerThreadListener(this::endServerThread);
                    synchronized (LOCK) {
                        mServerMap.put(mCurrentID, serverThread);
                        mCurrentID++;
                        mClientCounts++;
                    }
                    serverThread.start();
                }
            } while (!mEndFlag);
            mServerSocket.close();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            end();
        }
    }

    public final void end() {
        mUDPThread.end();
        mEndFlag = true;
        if (mServerSocket != null) {
            try {
                mServerSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public void setCreateServerThreadListener(CreateServerThreadListener createServerThreadListener) {
        mCreateServerThreadListener = createServerThreadListener;
    }

    public void setEndServerThreadListener(EndServerThreadListener endServerThreadListener) {
        mEndServerThreadListener = endServerThreadListener;
    }

    public String getRootPath() {
        return mRootPath;
    }

    public void setRootPath(String rootPath) {
        if (rootPath != null && rootPath.length() > 0) {
            File file = new File(rootPath);
            if (file.exists() && file.isDirectory()) {
                mRootPath = rootPath;
            }
        }
    }

    public int getPort() {
        return mPort;
    }

    public int getClientCounts() {
        synchronized (LOCK) {
            return mClientCounts;
        }
    }

    public long getCurrentID() {
        return mCurrentID;
    }

    protected ServerThread createServerThread(Socket clientSocket) {
        ServerThread serverThread = null;
        if (clientSocket != null) {
            serverThread = new ServerThread(clientSocket, mCurrentID, mRootPath);
        }
        return serverThread;
    }

    private void endServerThread(long id) {
        synchronized (LOCK) {
            if (mServerMap.containsKey(id)) {
                if (mEndServerThreadListener != null) {
                    mEndServerThreadListener.onServerThreadEnd(id);
                }
                mServerMap.remove(id);
                mClientCounts--;
            }
        }
    }
}